#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
身份证号码和手机号码生成器模块
提供生成符合规范的身份证号码和手机号码的功能
"""

import hashlib
import random
import sys
import os
import datetime
import time
import threading

# 尝试导入Python-Common-Tools中的工具类
try:
    sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../Python-Common-Tools'))
    from com.tools.Date_Utils.date_utils import DateUtils
    from com.tools.Data_Utils.data_utils import DataUtils
    from com.tools.Encode_Utils.encoder import Encoder
    TOOLS_AVAILABLE = True
except ImportError:
    DateUtils = None
    DataUtils = None
    Encoder = None
    TOOLS_AVAILABLE = False


class IDGenerator:
    """
    身份证号码和手机号码生成器类
    """
    
    # 省份代码映射表（部分）
    PROVINCE_CODES = {
        "11": "北京", "12": "天津", "13": "河北", "14": "山西", "15": "内蒙古",
        "21": "辽宁", "22": "吉林", "23": "黑龙江", "31": "上海", "32": "江苏",
        "33": "浙江", "34": "安徽", "35": "福建", "36": "江西", "37": "山东",
        "41": "河南", "42": "湖北", "43": "湖南", "44": "广东", "45": "广西",
        "46": "海南", "50": "重庆", "51": "四川", "52": "贵州", "53": "云南",
        "54": "西藏", "61": "陕西", "62": "甘肃", "63": "青海", "64": "宁夏",
        "65": "新疆", "71": "台湾", "81": "香港", "82": "澳门"
    }
    
    # 校验码映射表
    CHECK_CODE_MAP = {
        0: '1', 1: '0', 2: 'X', 3: '9', 4: '8', 5: '7', 6: '6', 7: '5', 8: '4', 9: '3', 10: '2'
    }
    
    # 身份证权重因子
    WEIGHT_FACTORS = [2, 4, 8, 5, 10, 9, 7, 3, 6, 1, 2, 4, 8, 5, 10, 9, 7]
    
    # 手机号前缀映射表（部分）
    MOBILE_PREFIXES = [
        "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
        "145", "147", "150", "151", "152", "153", "155", "156", "157", "158", "159",
        "170", "171", "173", "175", "176", "177", "178", "180", "181", "182", "183", "184",
        "185", "186", "187", "188", "189", "198", "199"
    ]
    
    # 交易流水号锁
    _transaction_lock = threading.Lock()
    _last_transaction_time = 0
    _last_transaction_sequence = 0
    
    @staticmethod
    def _calculate_check_digit(id_number):
        """
        计算身份证号码的校验码
        
        :param id_number: 17位身份证号码
        :return: 校验码
        """
        total = 0
        for i in range(17):
            total += int(id_number[i]) * IDGenerator.WEIGHT_FACTORS[i]
        remainder = total % 11
        return IDGenerator.CHECK_CODE_MAP[remainder]
    
    @staticmethod
    def generate_id_number(prefix="", age_range=[28, 48], max_attempts=1000, digit_conditions=None):
        """
        生成符合规范的18位身份证号码
        
        :param prefix: 身份证前缀（2位省份代码或6位地区代码）
        :param age_range: 年龄范围 [最小年龄, 最大年龄]，例如 [18, 60]
        :param max_attempts: 最大尝试次数
        :param digit_conditions: 数字条件列表，例如 [{"positions": [17, 16], "range": [10, 20]}, {"positions": [10, 14], "range": [50, 60]}]
        :return: 符合条件的身份证号码
        :raises RuntimeError: 当无法导入所需工具时抛出异常
        :raises ValueError: 当参数不符合要求时抛出异常
        """
        if not TOOLS_AVAILABLE:
            raise RuntimeError("无法导入Python-Common-Tools中的工具模块")
        
        if prefix and len(prefix) not in [2, 6]:
            raise ValueError("前缀必须是2位或6位数字")
        

        
        attempts = 0
        while attempts < max_attempts:
            # 处理年龄范围
            birth_date = ""
            if age_range:
                if len(age_range) != 2 or age_range[0] > age_range[1]:
                    raise ValueError("年龄范围必须是包含两个元素的列表，且第一个元素不大于第二个元素")

                # 计算对应的出生日期范围
                current_date = datetime.date.today()
                min_birth_date = DateUtils.add_age(current_date, -age_range[1])  # 最大年龄对应最早出生日期
                max_birth_date = DateUtils.add_age(current_date, -age_range[0])  # 最小年龄对应最晚出生日期

                # 生成指定范围内的随机日期
                random_date = DateUtils.random_date(min_birth_date, max_birth_date)
                birth_date = random_date.strftime("%Y%m%d")

            # 如果没有指定出生日期，则随机生成一个
            if not birth_date:
                # 生成1950-01-01到2005-12-31之间的随机日期
                start_date = datetime.date(1950, 1, 1)
                end_date = datetime.date(2005, 12, 31)
                random_date = DateUtils.random_date(start_date, end_date)
                birth_date = random_date.strftime("%Y%m%d")

            # 构建身份证号码
            id_number = ""
            
            # 添加前缀
            if not prefix:
                # 随机选择省份代码
                province_code = DataUtils.random_choice(list(IDGenerator.PROVINCE_CODES.keys()))
                id_number += province_code
                # 添加4位地区代码（随机）
                area_code = str(DataUtils.generate_true_random_number(1000, 9999))
                id_number += area_code
            elif len(prefix) == 2:
                # 使用指定的2位省份代码
                if prefix not in IDGenerator.PROVINCE_CODES:
                    raise ValueError("无效的省份代码")
                id_number += prefix
                # 添加4位地区代码（随机）
                area_code = str(DataUtils.generate_true_random_number(1000, 9999))
                id_number += area_code
            else:
                # 使用指定的6位地区代码
                id_number += prefix
            
            # 添加出生日期
            id_number += birth_date
            
            # 添加3位顺序码
            sequence_code = str(DataUtils.generate_true_random_number(0, 999)).zfill(3)
            id_number += sequence_code
            
            # 计算校验码
            check_digit = IDGenerator._calculate_check_digit(id_number)
            id_number += check_digit
            
            # 检查是否满足数字条件
            if not digit_conditions or IDGenerator._check_all_digit_conditions_md5(id_number, digit_conditions):
                return id_number
            
            # 如果不满足条件，重新生成整个身份证号码
            attempts += 1
        
        # 如果有数字条件但超过尝试次数，抛出异常
        if digit_conditions and attempts >= max_attempts:
            # 如果超过最大尝试次数，中断线程
            time.sleep(0.01)  # 短暂休眠模拟中断
            raise RuntimeError(f"无法在{max_attempts}次尝试内生成满足条件的身份证号码")
        
        return id_number
    
    @staticmethod
    def _check_all_digit_conditions_md5(id_number, conditions_list):
        """
        检查身份证号码MD5加密后是否满足所有指定的数字条件
        
        :param id_number: 身份证号码
        :param conditions_list: 条件列表
        :return: 是否满足所有条件
        """
        if not conditions_list:
            return True
        
        # 对身份证号码进行MD5加密
        md5_hash = Encoder.md5_encode(id_number)
        
        # 将MD5哈希转换为大数字（将所有字符转换为ASCII码并连接）
        md5_numeric = ""
        for char in md5_hash:
            md5_numeric += str(ord(char))
        print(f"md5tonumber{md5_numeric}")
        # 检查每个条件
        for conditions in conditions_list:
            if "positions" not in conditions or "range" not in conditions:
                continue
            
            positions = conditions["positions"]
            range_limit = conditions["range"]
            
            # 提取指定位置的数字并组合
            combined_digits = ""
            for pos in positions:
                # 处理负数索引（倒数位置）
                if pos < 0:
                    # 倒数第N位，例如-1表示倒数第1位
                    actual_pos = len(md5_numeric) + pos + 1
                else:
                    # 正数索引
                    actual_pos = pos
                
                # 检查位置是否在有效范围内
                if 1 <= actual_pos <= len(md5_numeric):
                    combined_digits += md5_numeric[actual_pos - 1]
                else:
                    # 位置超出范围，该条件作废，直接返回False
                    return False
            
            # 检查组合数字是否在指定范围内
            try:
                combined_value = int(combined_digits)
                if not (range_limit[0] <= combined_value <= range_limit[1]):
                    return False
            except ValueError:
                return False
        
        return True
    
    @staticmethod
    def generate_mobile_number(prefix="", max_attempts=1000, digit_conditions=None):
        """
        生成符合规范的11位手机号码
        
        :param prefix: 手机号前缀（3-6位）
        :param max_attempts: 最大尝试次数
        :param digit_conditions: 数字条件列表
        :return: 符合条件的手机号码
        :raises RuntimeError: 当无法导入所需工具时抛出异常
        :raises ValueError: 当参数不符合要求时抛出异常
        """
        if not TOOLS_AVAILABLE:
            raise RuntimeError("无法导入Python-Common-Tools中的工具模块")
        
        if prefix and not (3 <= len(prefix) <= 6):
            raise ValueError("前缀必须是3到6位数字")
        
        mobile_number = ""
        
        # 添加前缀
        if not prefix:
            # 随机选择手机号前缀
            mobile_prefix = DataUtils.random_choice(IDGenerator.MOBILE_PREFIXES)
            mobile_number += mobile_prefix
        else:
            mobile_number += prefix
        
        attempts = 0
        while attempts < max_attempts:
            mobile_number = ""
            
            # 添加前缀
            if not prefix:
                # 随机选择手机号前缀
                mobile_prefix = DataUtils.random_choice(IDGenerator.MOBILE_PREFIXES)
                mobile_number += mobile_prefix
            else:
                mobile_number += prefix
            
            # 补齐剩余位数
            remaining_length = 11 - len(mobile_number)
            if remaining_length > 0:
                # 生成剩余的随机数字
                for _ in range(remaining_length):
                    digit = str(DataUtils.generate_true_random_number(0, 9))
                    mobile_number += digit
            
            # 检查是否满足数字条件
            if not digit_conditions or IDGenerator._check_all_digit_conditions_md5(mobile_number, digit_conditions):
                return mobile_number
            
            # 如果不满足条件，重新生成整个手机号码
            attempts += 1
        
        # 如果有数字条件但超过尝试次数，抛出异常
        if digit_conditions and attempts >= max_attempts:
            # 如果超过最大尝试次数，中断线程
            time.sleep(0.01)  # 短暂休眠模拟中断
            raise RuntimeError(f"无法在{max_attempts}次尝试内生成满足条件的手机号码")
        
        return mobile_number
    
    @staticmethod
    def generate_transaction_number(prefix=""):
        """
        生成唯一的交易流水号
        
        :param prefix: 流水号前缀
        :return: 唯一的交易流水号
        :raises RuntimeError: 当无法导入所需工具时抛出异常
        """
        if not TOOLS_AVAILABLE:
            raise RuntimeError("无法导入Python-Common-Tools中的工具模块")
        
        # 使用线程锁确保多线程环境下的唯一性
        with IDGenerator._transaction_lock:
            current_time = int(time.time() * 1000)  # 毫秒级时间戳
            
            # 如果是同一毫秒内生成，需要增加序列号
            if current_time == IDGenerator._last_transaction_time:
                IDGenerator._last_transaction_sequence += 1
            else:
                IDGenerator._last_transaction_time = current_time
                IDGenerator._last_transaction_sequence = 0
            
            # 生成随机数
            random_num = DataUtils.generate_true_random_number(100000, 999999)
            
            # 组合生成流水号
            transaction_number = f"{prefix}{current_time}{IDGenerator._last_transaction_sequence:03d}{random_num}"
            
            return transaction_number